package com.symaster.gameengine.core;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL30;

import java.nio.ByteBuffer;

/**
 * FBO 组件
 *
 * @author yinmiao
 * @since 2024/9/9
 */
public abstract class FboComponent extends Component {

    /**
     * 当前FBO的尺寸
     */
    private int fboWidth = 0, fboHeight = 0;
    /**
     * 当前FBO
     */
    private int fbo = 0;
    /**
     * 当前FBO纹理
     */
    private int fboTexture = 0;
    /**
     * 设置当前组件的亮度
     */
    private Float brightness = null;
    /**
     * 设置当前组件的透明度
     */
    private Float alpha = null;

    /**
     * @return 返回当前组件的透明图
     */
    public Float getAlpha() {
        return alpha;
    }

    /**
     * 设置当前组件的透明度
     *
     * @param alpha 透明度
     */
    public void setAlpha(Float alpha) {
        this.alpha = alpha;
    }

    /**
     * @return 当前渲染Fbo使用的亮度
     */
    public Float getBrightness() {
        return brightness;
    }

    /**
     * 设置渲染Fbo使用的亮度
     *
     * @param brightness 目标亮度 0 ~ 1
     */
    public void setBrightness(float brightness) {
        this.brightness = brightness;
    }

    /**
     * @param fboWidth  指定宽度
     * @param fboHeight 指定高度
     * @return 是否更新
     */
    public boolean setupFBO(int fboWidth, int fboHeight) {
        // 指定尺寸必须大于0
        if (fboWidth <= 0) {
            throw new IllegalArgumentException("width must be greater than 0");
        }
        if (fboHeight <= 0) {
            throw new IllegalArgumentException("height must be greater than 0");
        }

        // 如果尺寸相等, 取消创建Fbo
        if (this.fboWidth == fboWidth && this.fboHeight == fboHeight) {
            return false;
        }

        // 设定当前Fbo尺寸
        this.fboWidth = fboWidth;
        this.fboHeight = fboHeight;

        // 如果已经有 FBO，先删除它
        if (this.fbo != 0) {
            GL30.glDeleteFramebuffers(this.fbo);
            GL11.glDeleteTextures(this.fboTexture);
        }

        // 创建 FBO
        this.fbo = GL30.glGenFramebuffers();
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, this.fbo);

        // 创建纹理，用作 FBO 的颜色附件
        this.fboTexture = GL11.glGenTextures();
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.fboTexture);
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, this.getFboWidth(), this.getFboHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);

        // 设置纹理参数
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);

        // 附加纹理到 FBO
        GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_TEXTURE_2D, this.fboTexture, 0);

        // 检查 FBO 完整性
        if (GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER) != GL30.GL_FRAMEBUFFER_COMPLETE) {
            throw new RuntimeException("Framebuffer is not complete!");
        }

        // 解除绑定 FBO, 回到默认缓冲区
        bindDefaultBuffer();

        return true;
    }

    /**
     * 绑定到默认缓冲区
     */
    public void bindDefaultBuffer() {
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
        GL11.glDisable(GL11.GL_BLEND);
        GL11.glPopAttrib();
    }

    /**
     * 绑定当前Fbo缓冲区
     */
    public void bindFbo() {
        // 绑定当前Fbo缓冲区
        GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, this.getFbo());
        // 设置当前Fbo缓冲区尺寸
        GL11.glViewport(0, 0, this.getFboWidth(), this.getFboHeight());
        // 启用混合
        GL11.glEnable(GL11.GL_BLEND);
        // 设置混合函数
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        // 记录当前状态
        GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_VIEWPORT_BIT);
    }

    /**
     * 绑定当前纹理
     */
    public void bindTexture() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, this.getFboTexture());
    }

    /**
     * @return 当前Fbo宽度
     */
    public int getFboWidth() {
        return fboWidth;
    }

    /**
     * @return 当前Fbo高度
     */
    public int getFboHeight() {
        return fboHeight;
    }

    /**
     * @return 当前Fbo缓冲区
     */
    public int getFbo() {
        return fbo;
    }

    /**
     * @return 当前Fbo纹理
     */
    public int getFboTexture() {
        return fboTexture;
    }

    /**
     * 绘制一个应用纹理的四边形
     */
    public void fboRenderScene() {
        fboRenderScene(false);
    }

    /**
     * 绘制一个应用纹理的四边形
     */
    public void fboRenderScene(boolean overturn) {
        GL11.glBegin(GL11.GL_QUADS);

        if (!overturn) {

            GL11.glTexCoord2f(0f, 0f);
            GL11.glVertex2f(-1f, -1f);
            GL11.glTexCoord2f(1f, 0f);
            GL11.glVertex2f(1f, -1f);
            GL11.glTexCoord2f(1f, 1f);
            GL11.glVertex2f(1f, 1f);
            GL11.glTexCoord2f(0f, 1f);
            GL11.glVertex2f(-1f, 1f);

        } else {
            GL11.glTexCoord2f(0f, 1f); // 调整纹理坐标，原 (0, 0) 改为 (0, 1)
            GL11.glVertex2f(-1f, -1f);

            GL11.glTexCoord2f(1f, 1f); // 调整纹理坐标，原 (1, 0) 改为 (1, 1)
            GL11.glVertex2f(1f, -1f);

            GL11.glTexCoord2f(1f, 0f); // 调整纹理坐标，原 (1, 1) 改为 (1, 0)
            GL11.glVertex2f(1f, 1f);

            GL11.glTexCoord2f(0f, 0f); // 调整纹理坐标，原 (0, 1) 改为 (0, 0)
            GL11.glVertex2f(-1f, 1f);
        }

        GL11.glEnd();

    }

    /**
     * 绑定并且绘制当前Fbo
     */
    public void renderTextureToScreen() {
        // 启用2D纹理渲染
        GL11.glEnable(GL11.GL_TEXTURE_2D);
        // 绑定当前Fbo的纹理
        this.bindTexture();
        // 绘制纹理
        this.fboRenderScene();
        // 关闭2D纹理渲染
        GL11.glDisable(GL11.GL_TEXTURE_2D);
    }

    public final float findBrightness() {
        if (getBrightness() == null) {
            final Component[] parentCache = new Component[1];

            parentCache[0] = getParent();
            if (parentCache[0] == null) {
                return 1f;
            }

            while (parentCache[0] != null) {
                if (parentCache[0] instanceof FboComponent parentFbo) {
                    if (parentFbo.getBrightness() != null) {
                        return parentFbo.getBrightness();
                    }
                }
                parentCache[0] = parentCache[0].getParent();
            }

            return 1f;
        }

        return getBrightness();
    }

    public final float findAlpha() {
        if (getAlpha() == null) {
            final Component[] parentCache = new Component[1];
            parentCache[0] = getParent();
            if (parentCache[0] == null) {
                return 1f;
            }
            while (parentCache[0] != null) {
                if (parentCache[0] instanceof FboComponent parentFbo) {
                    if (parentFbo.getAlpha() != null) {
                        return parentFbo.getAlpha();
                    }
                }
                parentCache[0] = parentCache[0].getParent();
            }
            return 1f;
        }
        return getAlpha();
    }

    /**
     * 清空
     */
    public void clear() {
        GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
    }

    /**
     * openGl draw
     */
    public abstract void repaint2Fbo();

}
